/****************************************************************************
 **
 ** This file is part of the yFiles extension package GraphML-3.2-yFiles-2.7.
 ** 
 ** yWorks proprietary/confidential. Use is subject to license terms.
 **
 ** Redistribution of this file or of an unauthorized byte-code version
 ** of this file is strictly forbidden.
 **
 ** Copyright (c) 2000-2009 by yWorks GmbH, Vor dem Kreuzberg 28, 
 ** 72070 Tuebingen, Germany. All rights reserved.
 **
 ***************************************************************************/

package demo.yext.graphml;

import y.base.DataMap;
import y.base.Edge;
import y.base.EdgeCursor;
import y.base.Node;
import y.base.NodeCursor;
import y.util.DataProviderAdapter;
import y.util.Maps;
import y.util.YRandom;
import y.view.Graph2D;
import y.view.HitInfo;
import y.view.ViewMode;

import javax.swing.Action;
import javax.swing.JMenu;
import java.awt.event.ActionEvent;
import java.util.HashSet;
import java.util.Set;

/**
 * This demo shows how to configure a GraphMLIOHandler to persist the value of GraphML core element IDs.
 */
public class GraphMLIdDemo extends GraphMLDemo {

  /**
   * This stores all ids of normal graph core elements, except for ids of nested graph
   * (this demo only handles flat graphs correctly, to keep it simple
   */
  DataMap elementIdMap = Maps.createHashedDataMap();

  /**
   * We use these sets to check that all id values are unique for the respective scope
   */
  Set nodeIds = new HashSet();
  Set edgeIds = new HashSet();
  Set graphIds = new HashSet();

  YRandom rnd = new YRandom();

  public GraphMLIdDemo() {
    getGraphMLIOHandler().setIdDataAcceptor(elementIdMap);
    //we wrap the original map, so that we can check for duplicate ids or generate ids for new elements
    getGraphMLIOHandler().setIdDataProvider(new DataProviderAdapter() {
      public Object get(Object dataHolder) {
        Object origId = elementIdMap.get(dataHolder);
        if (origId == null) {
          //this is a new element which has no id that has been set previously, so generate one
          String generatedId = null;
          if (dataHolder instanceof Node) {
            generatedId = generateId(nodeIds, "node_generated_");
          } else if (dataHolder instanceof Edge) {
            generatedId = generateId(edgeIds, "edge_generated_");
          } else {
            generatedId = generateId(graphIds, "graph_generated_");
          }
          elementIdMap.set(dataHolder, generatedId);
          return generatedId;
        } else {
          return origId;
        }
      }

      private String generateId(Set ids, String prefix) {
        String id;
        do {
          id = prefix + rnd.nextInt(0, Integer.MAX_VALUE);
        }
        while (ids.contains(id));
        ids.add(id);
        return id;
      }
    });
    //define a view mode that displays the element's ID
    ViewMode tooltipMode = new ViewMode() {
      public void mouseMoved(double x, double y) {
        HitInfo info = getHitInfo(x, y);
        String id = "";
        if (info.getHitNode() != null) {
          id = (String) elementIdMap.get(info.getHitNode());
        } else if (info.getHitEdge() != null) {
          id = (String) elementIdMap.get(info.getHitEdge());
        } else {
          //retrieve the id of the view's root graph
          id = (String) elementIdMap.get(view.getGraph2D());
        }
        view.setToolTipText("ID=" + id);
      }
    };

    //add the view mode to the view
    view.addViewMode(tooltipMode);
  }

  protected Action createSaveAction() {
    return new SaveAction();
  }

  /**
   * Action that saves the current graph to a file in GraphML format.
   */
  class SaveAction extends GraphMLDemo.SaveAction {
    public void actionPerformed(ActionEvent e) {
      //we decorate the base class action, to clear the id sets before each write process
      Graph2D graph = view.getGraph2D();
      nodeIds.clear();
      for (NodeCursor nodeCursor = graph.nodes(); nodeCursor.ok(); nodeCursor.next()) {
        Node node = nodeCursor.node();
        Object o = elementIdMap.get(node);
        if (o != null) {
          nodeIds.add(o);
        }
      }

      edgeIds.clear();
      for (EdgeCursor edgeCursor = graph.edges(); edgeCursor.ok(); edgeCursor.next()) {
        Edge edge = edgeCursor.edge();
        Object o = elementIdMap.get(edge);
        if (o != null) {
          edgeIds.add(o);
        }
      }
      graphIds.clear();
      graphIds.add(elementIdMap.get(graph));
      super.actionPerformed(e);
    }
  }

  /**
   * Add sample graphs to the menu.
   */
  protected JMenu createSampleGraphMenu() {
    return createSampleGraphMenu(new String[]{"resources/simple/simple.graphml"});
  }

  /**
   * Launches this demo.
   */
  public static void main(String[] args) {
    initLnF();
    final GraphMLIdDemo demo = new GraphMLIdDemo();
    demo.start();
  }
}
